package sunwin.yoms.dao.extend;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import jodd.datetime.JDateTime;
import jodd.util.StringUtil;
import org.apache.commons.lang.NullArgumentException;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.SqlSession;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.stereotype.Repository;
import sunwin.yoms.comm.YomsException;
import sunwin.yoms.comm.YomsPage;
import sunwin.yoms.comm.YomsLogSubregion;
import sunwin.yoms.dao.comm.MybatisHelper;
import sunwin.yoms.domain.comm.AdminOperateDomain;

import java.io.IOException;
import java.util.*;

import static sunwin.yoms.dao.comm.JDBCHelper.*;

/**
 * Created by 田西泽 on 2015-8-27.
 */
@Repository
public class AdminOperateDao {
    private long maxTotal = 10000 * 1000;
    private String propertiesName = "sunwin.yoms.properties";

    /**
     * 获取日志
     *
     * @param domain
     */
    public Collection<AdminOperateDomain> getLogs(AdminOperateDomain domain,
                                                  YomsPage yomsPage,
                                                  YomsLogSubregion subregion,
                                                  @Param("startTime") Date startTime,
                                                  @Param("endTime") Date endTime) throws IOException, YomsException {
        Collection<AdminOperateDomain> domains = new ArrayList<>();
        SqlSession session = MybatisHelper.getSqlSession();

        if (session != null) {
            if (subregion == null) {
                throw new NullArgumentException("日志分区对象不能为空");
            }
            if (yomsPage == null) {
                yomsPage = new YomsPage();
            }

            List<String> tableNames = getTableNames(this.getConnectionString(propertiesName), this.getDataBaseName());
            String tableName = tableNames.get(subregion.getSubregionIndex());

            Map<String, Object> map = new HashMap<>();
            map.put("domain", domain);
            map.put("startTime", startTime);
            map.put("endTime", endTime);
            map.put("tableName", tableName);

            try {
                PageHelper.orderBy("admin_operate_datetime desc");
                Page page = PageHelper.startPage(yomsPage.getPageIndex(), yomsPage.getPageSize());
                domains = session.selectList("getDomainCollection", map);
                yomsPage.setPageCount(page.getPages());
            } finally {
                session.close();
            }
        }

        return domains;
    }

    /**
     * 获取日志分区
     *
     * @return
     */
    public YomsLogSubregion getLogSubregion() throws Exception {
        List<String> tableNames = getTableNames(this.getConnectionString(propertiesName), this.getDataBaseName());
        YomsLogSubregion subregion = new YomsLogSubregion();
        subregion.setSubregionTotal(tableNames.size());
        //获取最后一个分区
        subregion.setSubregionIndex(tableNames.size() - 1);
        return subregion;
    }

    /**
     * 插入日志
     *
     * @param domain
     * @throws YomsException
     */
    public synchronized String addLog(AdminOperateDomain domain) throws YomsException {
        String tableName = getLastTableName(maxTotal);
        SqlSession session = MybatisHelper.getSqlSession();
        Map<String, Object> map = new HashMap<>();
        map.put("domain", domain);
        map.put("tableName", tableName);
        try {
            int result = session.insert("addDomain", map);
            if (result != 1) {
                throw YomsException.getInstance("插入日志失败");
            }
            session.commit();
        } finally {
            session.close();
        }
        return tableName;
    }

    /**
     * 更新日志
     * @param domain
     * @param tableName
     */
    public synchronized void updateLog(AdminOperateDomain domain, String tableName) {
        SqlSession session = MybatisHelper.getSqlSession();
        Map<String, Object> map = new HashMap<>();
        map.put("domain", domain);
        map.put("tableName", tableName);
        try {
            int result = session.update("updateDomain", map);
            if (result != 1) {
                throw YomsException.getInstance("更新日志失败");
            }
            session.commit();
        } catch (YomsException e) {
            e.printStackTrace();
        } finally {
            session.close();
        }
    }

    /**
     * 获取最后保存日志的表名
     * 如果数据库没有表时自动创建
     * 如果表中的记录大于指定数据条目时创建新表
     *
     * @return
     */
    private String getLastTableName(long maxNumber) throws YomsException {
        String tableName = null;

        Map<String, Object> map = new HashMap<>();
        map.put("databaseName", getDataBaseName());
        List<String> tableNames = getTableNames(this.getConnectionString(propertiesName), this.getDataBaseName());
        if (tableNames.size() == 0) {
            //创建数据库中第一张表
            tableName = buildTableName();
            createTable(tableName);
        } else {
            tableName = tableNames.get(tableNames.size() - 1);

            long dataTotal = getTableDataCount(tableName);

            if (dataTotal >= maxNumber) {
                tableName = buildTableName();
                createTable(tableName);
            }
        }

        if (tableName == null) {
            throw YomsException.getInstance("获取最后一张表的表名失败");
        }

        return tableName;
    }

    /**
     * 获取最后一张表的数据总数量
     *
     * @param tableName
     * @return
     */
    private int getTableDataCount(String tableName) throws YomsException {
        Map<String, Object> map = new HashMap<>();
        map.put("tableName", tableName);
        SqlSession session = MybatisHelper.getSqlSession();
        int count;
        try {
            count = session.selectOne("getCount", map);
        } finally {
            session.close();
        }

        return count;
    }

    /**
     * 动态创建表
     *
     * @param tableName
     */
    private void createTable(String tableName) throws YomsException {
        Map<String, Object> map = new HashMap<>();
        map.put("tableName", tableName);
        SqlSession session = MybatisHelper.getSqlSession();
        try {
            session.update("createLogTable", map);

            map.put("databaseName", getDataBaseName());
            boolean isExist = tableIsExist(this.getConnectionString(propertiesName), this.getDataBaseName(), tableName);

            if (!isExist) {
                throw YomsException.getInstance("创建新表失败");
            }
        } finally {
            session.close();
        }
    }

    /**
     * 生成一个当前数据库中唯一的表名
     */
    private String buildTableName() {
        JDateTime dateTime = new JDateTime();
        return "log_" + dateTime.toString("YYYYMMDDhhmmssmss");
    }

    /**
     * 获取日志数据库名称
     *
     * @return
     * @throws IOException
     */
    private String getDataBaseName() throws YomsException {
        ClassPathResource resource = new ClassPathResource(propertiesName);
        Properties props = null;
        String dbName = null;
        try {
            props = PropertiesLoaderUtils.loadProperties(resource);
            dbName = props.getProperty("jdb.database");
            if (StringUtil.isBlank(dbName)) {
                throw new Exception();
            }
        } catch (Exception e) {
            throw YomsException.getInstance("获取日志数据库名称失败");
        }
        return dbName + ".log";
    }

    /**
     * 获取连接字符串
     *
     * @return
     */
    private String getConnectionString(String propertiesName) throws YomsException {
        ClassPathResource resource = new ClassPathResource(propertiesName);
        Properties props = null;
        try {
            props = PropertiesLoaderUtils.loadProperties(resource);
        } catch (Exception e) {
            throw YomsException.getInstance("获取连接字符串失败");
        }
        String address = props.get("jdbc.address").toString();
        String port = props.get("jdbc.port").toString();
        String database = props.get("jdb.database").toString() + ".log";
        String user = props.get("jdbc.user").toString();
        String password = props.get("jdbc.password").toString();

        String connString = getConnString(address, port, database, user, password);

        return connString;
    }
}
